package com.dms_back1.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dms_back1.bean.constants.ErrorCode;
import com.dms_back1.bean.entity.User;
import com.dms_back1.bean.enums.VerifyCodeType;
import com.dms_back1.bean.req.LoginReq;
import com.dms_back1.bean.req.RegisterReq;
import com.dms_back1.bean.req.ThirdPartyLoginReq;
import com.dms_back1.bean.res.LoginRes;
import com.dms_back1.ex.BusinessException;
import com.dms_back1.mapper.UserMapper;
import com.dms_back1.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    
    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    private final StringRedisTemplate redisTemplate;
    
    // Redis key前缀
    private static final String VERIFY_CODE_KEY_PREFIX = "verify_code:";
    // 验证码有效期（分钟）
    private static final long VERIFY_CODE_EXPIRE_MINUTES = 5;
    
    public UserServiceImpl(UserMapper userMapper,
                          PasswordEncoder passwordEncoder,
                          StringRedisTemplate redisTemplate) {
        this.userMapper = userMapper;
        this.passwordEncoder = passwordEncoder;
        this.redisTemplate = redisTemplate;
    }
    //发送验证码
    @Override
    public void sendVerificationCode(String phoneNumber, VerifyCodeType type) {
        // 根据不同的验证码类型进行不同的验证
        if (type == VerifyCodeType.REGISTER) {
            // 注册场景：验证手机号是否已注册
            if (isPhoneNumberRegistered(phoneNumber)) {
                throw new BusinessException("手机号已注册");
            }
        }
        // 绑定手机号场景：不需要验证，允许发送验证码
        
        // 检查验证码发送频率
        String countKey = "verify_code_count:" + phoneNumber;
        String countStr = redisTemplate.opsForValue().get(countKey);
        int count = countStr == null ? 0 : Integer.parseInt(countStr);
        if (count >= 80) {
            throw new BusinessException("发送验证码过于频繁，请稍后再试");
        }
        
        // 生成验证码
        String code = generateVerificationCode();
        
        // 将验证码存入Redis，设置5分钟过期
        String redisKey = VERIFY_CODE_KEY_PREFIX + type.name() + ":" + phoneNumber;
        redisTemplate.opsForValue().set(redisKey, code, VERIFY_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        
        // 更新发送次数
        if (count == 0) {
            // 第一次发送，设置24小时过期
            redisTemplate.opsForValue().set(countKey, "1", 24, TimeUnit.HOURS);
        } else {
            // 增加发送次数
            redisTemplate.opsForValue().increment(countKey);
        }
        
        // 打印验证码（实际环境中应该通过短信发送）
        log.info("手机号：{}，验证码类型：{}，验证码：{}", phoneNumber, type.getDescription(), code);
    }
    //注册
    @Override
    @Transactional
    public void register(RegisterReq req) {
        // 验证手机号是否已注册
        if (isPhoneNumberRegistered(req.getPhoneNumber())) {
            throw new BusinessException("手机号已注册");
        }
        
        // 验证验证码
        String redisKey = VERIFY_CODE_KEY_PREFIX + VerifyCodeType.REGISTER.name() + ":" + req.getPhoneNumber();
        String savedCode = redisTemplate.opsForValue().get(redisKey);
        if (savedCode == null || !savedCode.equals(req.getCode())) {
            throw new BusinessException("验证码错误或已过期");
        }
        
        // 创建用户
        User user = new User();
        user.setPhoneNumber(req.getPhoneNumber());
        user.setPassword(passwordEncoder.encode(req.getPassword()));
        user.setNickname(req.getNickname());
        user.setIsPhoneVerified(true);
        userMapper.insert(user);
        
        // 删除验证码
        redisTemplate.delete(redisKey);
    }
    //登录
    @Override
    public LoginRes login(LoginReq req) {
        // 查询用户
        User user = userMapper.selectOne(
            new LambdaQueryWrapper<User>()
                .eq(User::getPhoneNumber, req.getPhoneNumber())
        );
        
        if (user == null) {
            throw new BusinessException("用户不存在");
        }
        
        // 验证密码
        if (!passwordEncoder.matches(req.getPassword(), user.getPassword())) {
            throw new BusinessException("密码错误");
        }
        
        // 生成token
        String token = generateToken(user);
        
        // 构建响应
        return buildLoginRes(user, token);
    }
    //第三方登录【授权信息】
    @Override
    @Transactional
    public LoginRes thirdPartyLogin(ThirdPartyLoginReq req) {
        // TODO: 根据不同的登录类型调用不同的第三方服务获取用户信息
        String openid = "test_openid"; // 这里需要替换为实际的openid
        String nickname = "test_nickname"; // 这里需要替换为实际的昵称
        String avatarUrl = "test_avatar_url"; // 这里需要替换为实际的头像URL
        
        // 查询用户是否存在
        User user = userMapper.selectOne(
            new LambdaQueryWrapper<User>()
                .eq(User::getWechatOpenid, openid)
                .or()
                .eq(User::getQqOpenid, openid)
        );
        
        if (user == null) {
            // 创建新用户
            user = new User();
            user.setNickname(nickname);
            user.setAvatarUrl(avatarUrl);
            if ("WECHAT".equals(req.getLoginType())) {
                user.setWechatOpenid(openid);
            } else {
                user.setQqOpenid(openid);
            }
            userMapper.insert(user);
        }
        
        // 生成token
        String token = generateToken(user);
        
        // 构建响应
        return buildLoginRes(user, token);
    }
    //绑定手机号！！，涉及账号合并，保证并发，数据库
    @Override
    @Transactional
    public void bindPhoneNumber(Long userId, String phoneNumber, String code) {
        int maxRetries = 3;//试三次
        int retryCount = 0;
        
        while (retryCount < maxRetries) {
            try {
                doBindPhoneNumber(userId, phoneNumber, code);
                return; // 成功后直接返回
            } catch (Exception e) {
                retryCount++;
                if (retryCount == maxRetries) {
                    log.error("Failed to bind phone number after {} retries", maxRetries, e);
                    throw new BusinessException("绑定手机号失败，请稍后重试");
                }
                log.warn("Retry {} to bind phone number", retryCount);
            }
        }
    }
    //绑定的实际方法
    @Transactional
    protected void doBindPhoneNumber(Long userId, String phoneNumber, String code) {
        // 验证验证码
        String redisKey = VERIFY_CODE_KEY_PREFIX + VerifyCodeType.BIND_PHONE.name() + ":" + phoneNumber;
        String savedCode = redisTemplate.opsForValue().get(redisKey);
        if (savedCode == null || !savedCode.equals(code)) {
            throw new BusinessException("验证码错误或已过期");
        }

        log.info("开始绑定手机号: userId={}, phoneNumber={}", userId, phoneNumber);

        // 查询当前用户（第三方登录用户）
        User currentUser = userMapper.selectById(userId);
        if (currentUser == null) {
            throw new BusinessException("用户不存在");
        }

        // 查询是否存在使用该手机号的用户，并加锁
        User phoneUser = userMapper.selectOne(
            new LambdaQueryWrapper<User>()
                .eq(User::getPhoneNumber, phoneNumber)
                .last("FOR UPDATE")
        );

        if (phoneUser != null) {
            // 如果找到手机号用户，且不是当前用户
            if (!phoneUser.getId().equals(userId)) {
                log.info("发现已存在的手机号用户，开始账号合并流程: phoneUserId={}, currentUserId={}", 
                    phoneUser.getId(), currentUser.getId());

                // 检查手机号用户是否已绑定第三方账号
                boolean hasThirdPartyAccount = phoneUser.getWechatOpenid() != null || phoneUser.getQqOpenid() != null;
                
                if (!hasThirdPartyAccount) {
                    log.info("手机号用户未绑定第三方账号，执行合并: phoneUserId={}", phoneUser.getId());
                    
                    // 设置手机号验证状态
                    phoneUser.setIsPhoneVerified(true);
                    
                    // 合并账号信息
                    mergeAccounts(phoneUser, currentUser);
                    
                    // 删除第三方登录用户
                    userMapper.deleteById(currentUser.getId());
                    log.info("合并完成，已删除第三方登录用户: userId={}", currentUser.getId());
                } else {
                    // 如果手机号用户已绑定第三方账号，抛出异常
                    log.error("手机号用户已绑定第三方账号，无法合并: phoneUserId={}, wechatOpenid={}, qqOpenid={}", 
                        phoneUser.getId(), phoneUser.getWechatOpenid(), phoneUser.getQqOpenid());
                    throw new BusinessException("该手机号已被其他用户绑定且验证");
                }
            } else {
                // 如果是同一个用户，只需更新验证状态
                log.info("当前用户已有该手机号，更新验证状态: userId={}", userId);
                phoneUser.setIsPhoneVerified(true);
                userMapper.updateById(phoneUser);
            }
        } else {
            // 如果手机号未被使用，直接绑定到当前用户
            log.info("手机号未被使用，直接绑定到当前用户: userId={}", userId);
            currentUser.setPhoneNumber(phoneNumber);
            currentUser.setIsPhoneVerified(true);
            userMapper.updateById(currentUser);
        }

        // 删除验证码
        redisTemplate.delete(redisKey);
        log.info("手机号绑定完成: userId={}, phoneNumber={}", userId, phoneNumber);
    }

    private void mergeAccounts(User phoneUser, User thirdPartyUser) {
        log.info("开始合并用户信息: phoneUserId={}, thirdPartyUserId={}", phoneUser.getId(), thirdPartyUser.getId());

        try {
            // 1. 合并微信账号
            if (thirdPartyUser.getWechatOpenid() != null) {
                handleThirdPartyAccountMerge(phoneUser, thirdPartyUser.getWechatOpenid(), true);
            }

            // 2. 合并QQ账号
            if (thirdPartyUser.getQqOpenid() != null) {
                handleThirdPartyAccountMerge(phoneUser, thirdPartyUser.getQqOpenid(), false);
            }

            // 3. 合并其他信息（只在目标用户对应字段为空时才合并）
            if (phoneUser.getNickname() == null && thirdPartyUser.getNickname() != null) {
                phoneUser.setNickname(thirdPartyUser.getNickname());
                log.info("合并昵称信息: phoneUserId={}, nickname={}", phoneUser.getId(), thirdPartyUser.getNickname());
            }
            if (phoneUser.getAvatarUrl() == null && thirdPartyUser.getAvatarUrl() != null) {
                phoneUser.setAvatarUrl(thirdPartyUser.getAvatarUrl());
                log.info("合并头像信息: phoneUserId={}, avatarUrl={}", phoneUser.getId(), thirdPartyUser.getAvatarUrl());
            }
            if (phoneUser.getPassword() == null && thirdPartyUser.getPassword() != null) {
                phoneUser.setPassword(thirdPartyUser.getPassword());
                log.info("合并密码信息: phoneUserId={}", phoneUser.getId());
            }

            // 4. 更新手机号用户
            userMapper.updateById(phoneUser);
            log.info("用户信息合并完成: phoneUserId={}", phoneUser.getId());
        } catch (Exception e) {
            log.error("合并用户信息失败: phoneUserId={}, thirdPartyUserId={}", phoneUser.getId(), thirdPartyUser.getId(), e);
            throw new BusinessException("合并用户信息失败，请稍后重试");
        }
    }

    private void handleThirdPartyAccountMerge(User phoneUser, String openid, boolean isWechat) {
        String accountType = isWechat ? "微信" : "QQ";
        log.info("处理{}账号合并: phoneUserId={}, openid={}", accountType, phoneUser.getId(), openid);

        try {
            // 1. 检查是否已有其他用户绑定了该账号
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            if (isWechat) {
                wrapper.eq(User::getWechatOpenid, openid);
            } else {
                wrapper.eq(User::getQqOpenid, openid);
            }
            wrapper.ne(User::getId, phoneUser.getId()).last("FOR UPDATE");

            User existingUser = userMapper.selectOne(wrapper);
            if (existingUser != null) {
                // 检查该用户是否已验证手机号
                if (existingUser.getIsPhoneVerified()) {
                    log.error("{}账号已被其他已验证用户绑定: userId={}, openid={}", 
                        accountType, existingUser.getId(), openid);
                    throw new BusinessException("该" + accountType + "账号已被其他用户绑定");
                } else {
                    log.info("{}账号被未验证用户绑定，允许合并: userId={}, openid={}", 
                        accountType, existingUser.getId(), openid);
                        //先储存账号信息
                        String originalOpenId = isWechat ? existingUser.getWechatOpenid() : existingUser.getQqOpenid();
                        String originalNickname = existingUser.getNickname();
                        String originalAvatarUrl = existingUser.getAvatarUrl();
                        String originalPassword = existingUser.getPassword();
                         // 删除未验证的用户
                    userMapper.deleteById(existingUser.getId());
                    log.info("已删除未验证的{}账号用户: userId={}", accountType, existingUser.getId());

                    if (isWechat) {
                        phoneUser.setWechatOpenid(openid);
                    } else {
                        phoneUser.setQqOpenid(openid);
                    }
                    userMapper.updateById(phoneUser);
                }
            }
            userMapper.updateById(phoneUser);
            log.info("已设置新的{}绑定: phoneUserId={}, openid={}", accountType, phoneUser.getId(), openid);
        } catch (Exception e) {
            log.error("处理{}账号合并失败: phoneUserId={}, openid={}", accountType, phoneUser.getId(), openid, e);
            throw new BusinessException("处理" + accountType + "账号合并失败，请稍后重试");
        }
    }
    
    private boolean isPhoneNumberRegistered(String phoneNumber) {
        return userMapper.selectCount(
            new LambdaQueryWrapper<User>()
                .eq(User::getPhoneNumber, phoneNumber)
        ) > 0;
    }
    
    private String generateVerificationCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
    
    private String generateToken(User user) {
        // TODO: 实现JWT token生成
        return "test_token";
    }
    
    private LoginRes buildLoginRes(User user, String token) {
        LoginRes res = new LoginRes();
        res.setToken(token);
        
        LoginRes.UserInfo userInfo = new LoginRes.UserInfo();
        userInfo.setId(user.getId());
        userInfo.setPhoneNumber(user.getPhoneNumber());
        userInfo.setNickname(user.getNickname());
        userInfo.setAvatarUrl(user.getAvatarUrl());
        userInfo.setIsPhoneVerified(user.getIsPhoneVerified());
        
        res.setUserInfo(userInfo);
        return res;
    }
} 